package com.yc.cinema;

import com.sun.org.apache.bcel.internal.util.ClassPath;
import com.yc.cinema.biz.UserBiz;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyAnnotationConfigApplicationContext {

    private Map<String, Object> ioc = new HashMap<>();

    public MyAnnotationConfigApplicationContext(Class<?> cls) {

        //判断是否是配置类
        Configuration configuration = cls.getAnnotation(Configuration.class);
        if (configuration == null) {
            throw new RuntimeException("该对象不是配置类!");
        }

        //读取组件扫描
        ComponentScan componentScan = cls.getAnnotation(ComponentScan.class);
        String[] value = componentScan.value();

        //扫描组件
        //创建组件对象,存入IOC容器中
        scanBean(value);
        //验证
        ioc.forEach((id, bean) -> {
            System.out.println(id + ": " + bean);
        });

        //自动装配
        autowire();
    }


    void scanBean(String[] packages) {
        //com.yc.cinema
        URL url = getClass().getClassLoader().getResource("");
        String root = url.getPath().substring((1));
        for (String apackage : packages) {
            String path = url.getPath();
            try {
                path = URLDecoder.decode(path, "UTF-8");
                root = URLDecoder.decode(root, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            //将包路径改成文件路径
            apackage = apackage.replaceAll("\\.", "/");
            //拼接path  ==》  扫描文件夹路径
            path += apackage;
            scanPackage(path.substring((1)), root);
            //    //File   ==>  文件夹  ==》进入文件夹扫为你教案
            //    //             文件   ==》class
            //     //                  //   类路径
            //     //                    ==》反射  创建对象
        }
    }

    void scanPackage(String path, String root) {
        File file = new File(path);
        System.out.println(file.getAbsoluteFile());
        if (file.isDirectory()) {
            System.out.println("扫描文件夹：" + file.getAbsoluteFile());
            for (File f : file.listFiles()) {
                scanPackage(f.getAbsolutePath(), root);
            }
        } else if (file.isFile()) {
            // System.out.println("创建:" + file.getAbsoluteFile());
            createBean(file.getAbsolutePath(), root);
        } else {
            //新文件对象
        }
    }

    void createBean(String classPath, String root) {
        root = root.replaceAll("/", "\\\\");
        classPath = classPath.replaceAll(root, "");
        classPath = classPath.replace(".class", "");
        classPath = classPath.replaceAll("\\\\", ".");
        System.out.println(classPath);
        try {
            Class<?> aClass = Class.forName(classPath);
            //判断是否是组件 =》组件
            Service service = aClass.getAnnotation(Service.class);
            String id = "";
            boolean isComponent = false;
            if (service != null) {
                id = service.value();
                isComponent = true;
            } else {
                Repository repository = aClass.getAnnotation(Repository.class);
                if (repository != null) {
                    id = repository.value();
                    isComponent = true;
                }
                //Contorller 和 Component请拓展完成
            }
            if (isComponent) {
                //如果是  ==》反射创建对象 ==》  默认使用类名首字母小写,作为bean 的id
                Object bean = aClass.newInstance();
                if ("".equals(id)) {
                    id = aClass.getSimpleName();
                    id = id.substring(0, 1).toLowerCase() + id.substring(1);
                }
                //存入ioc
                ioc.put(id, bean);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    //自动装配
    void autowire() {
        //遍历所有bean
        for (Object bean : ioc.values()) {
            //获取bean全部属性 （包含继承的属性）
            //bean.getClass().getFilds();公有属性
            //bean.getClass().getDeclaredFileids()//定义的属性
            List<Field> list = new ArrayList<>();
            getFileid(bean.getClass(), list);
            //判断注解Autowired byType  和 Resource byName
            try {
                Object obj = null;
                for (Field field : list){
                    if ((field.getAnnotation(Autowired.class) != null)){
                        System.out.println("byType 注入" + field);
                        //getType  == > userBiz  UserDao
                        //getClass ==>Field
                        obj = getBean(field.getType(),field);
                        field.setAccessible(true);
                        //从容器获取bean通过反射注入
                        field.set(bean,obj);
                    }else if((field.getAnnotation(Resource.class) != null)){
                        System.out.println("byName 注入" + field);
                        obj = getBean(field.getName());
                        field.setAccessible(true);
                        //从容器获取bean通过反射注入
                        field.set(bean,obj);
                    }
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //获取cls的全部属性
    void getFileid(Class<?> cls, List<Field> list) {
        for (Field field : cls.getDeclaredFields()) {
            list.add(field);
        }
        //获取父类
        cls = cls.getSuperclass();
        if (!cls.isAssignableFrom(Object.class)) {
            getFileid(cls, list);
        }
    }

    private <T> T getBean(Class<T> cls, Field field) {
        List<Object> beans = new ArrayList<>();
        //获取ico中该类所有bean
        for (Object bean : ioc.values()) {
            if (bean.getClass().isAssignableFrom(cls)) {
                beans.add(bean);
            }
        }
        if (beans.isEmpty()) {
            throw new RuntimeException("容器中没有该bean:" + cls);
        } else if (beans.size() == 1) {
            return (T) beans.get(0);
        } else {
            //多个bean
            //限定词注解
            Qualifier qualifier = field.getAnnotation(Qualifier.class);
            if (qualifier != null) {
                String id = qualifier.value();
                return (T) getBean(id);
            }else {
                //判断beans那一个加了Prymary注解
                throw new RuntimeException("容器中包含多个此类型的的Bean" + cls);
            }
        }
    }

    Object getBean (String id){
        Object bean = ioc.get(id);
        if (bean == null){
            throw new RuntimeException("没有该bean ,id = " + id);
        }
        return bean;
    }

    public static void main(String[] args) {
        MyAnnotationConfigApplicationContext ctx = new MyAnnotationConfigApplicationContext(AutoIocConfig.class);
        UserBiz userBiz = (UserBiz) ctx.getBean("userBiz");
        System.out.println(userBiz.getUdao());
    }
}
